#!/usr/bin/env python
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import json
import logging
import os
import subprocess
import sys

import yaml


CONF_FILE = os.environ.get('HEAT_SHELL_CONFIG',
                           '/var/run/heat-config/heat-config')

DOCKER_COMPOSE_DIR = os.environ.get(
    'HEAT_DOCKER_COMPOSE_WORKING',
    '/var/lib/heat-config/heat-config-docker-compose')

DOCKER_COMPOSE_CMD = os.environ.get('HEAT_DOCKER_COMPOSE_CMD',
                                    'docker-compose')


def main(argv=sys.argv):
    log = logging.getLogger('heat-config')
    handler = logging.StreamHandler(sys.stderr)
    handler.setFormatter(
        logging.Formatter(
            '[%(asctime)s] (%(name)s) [%(levelname)s] %(message)s'))
    log.addHandler(handler)
    log.setLevel('DEBUG')

    if not os.path.exists(CONF_FILE):
        log.error('No config file %s' % CONF_FILE)
        return 1

    if not os.path.isdir(DOCKER_COMPOSE_DIR):
        os.makedirs(DOCKER_COMPOSE_DIR, 0o700)

    try:
        configs = json.load(open(CONF_FILE))
    except ValueError:
        pass

    try:
        cleanup_stale_projects(configs)
        for c in configs:
            write_compose_config(c)
    except Exception as e:
        log.exception(e)


def cleanup_stale_projects(configs):
    def deployments(configs):
        for c in configs:
            yield c['name']

    def compose_projects(compose_dir):
        for proj in os.listdir(compose_dir):
            if os.path.isfile(
                    os.path.join(DOCKER_COMPOSE_DIR,
                                 '%s/docker-compose.yml' % proj)):
                yield proj

    def cleanup_containers(project):
        cmd = [
            DOCKER_COMPOSE_CMD,
            'kill'
        ]
        subproc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
        stdout, stderr = subproc.communicate()

    for proj in compose_projects(DOCKER_COMPOSE_DIR):
        if proj not in deployments(configs):
            proj_dir = os.path.join(DOCKER_COMPOSE_DIR, proj)
            os.chdir(proj_dir)
            cleanup_containers(proj)
            os.remove('%s/docker-compose.yml' % proj_dir)


def write_compose_config(c):
    group = c.get('group')
    if group != 'docker-compose':
        return

    def prepare_dir(path):
        if not os.path.isdir(path):
            os.makedirs(path, 0o700)

    compose_conf = c.get('config', '')
    if isinstance(compose_conf, dict):
        yaml_config = yaml.safe_dump(compose_conf, default_flow_style=False)
    else:
        yaml_config = compose_conf
    proj_dir = os.path.join(DOCKER_COMPOSE_DIR, c['name'])
    prepare_dir(proj_dir)
    fn = os.path.join(proj_dir, 'docker-compose.yml')
    with os.fdopen(os.open(fn, os.O_CREAT | os.O_WRONLY | os.O_TRUNC, 0o600),
                   'w') as f:
        f.write(yaml_config.encode('utf-8'))


if __name__ == '__main__':
    sys.exit(main(sys.argv))
